/**
 * This jQuery pagination plugin is folk from jquery.pagination.js (Gabriel Birke)
 *
 * This plugin needs at least jQuery 1.4.2
 *
 * @author levin
 * @version 1.0
 * @param {int} maxentries Number of entries to paginate
 * @param {Object} opts Several options (see README for documentation)
 * @return {Object} jQuery Object
 */
(function($){
	/**
	 * @class Factory Class for calculating pagination values and renderring the pager UI
	 * @param {int} cntItem the count of items to be paged.
	 * @param {Object} opts other options
	 */
	$.vPager=function(cntItem,opts){
		this.cntItem=cntItem;
		this.opts=opts;
	};
	/**
	 * eval a template with specified data
	 * @param {String} template html strings
	 * @param {Object} data json data
	 */
	$.vPager.ParseTpl = function(str, data) {
		///<summary>
		///simple js template parser
		///E.G,IF:str="<a href=/u/%uid%>%username%</a>"
		///data={uid:1,username:'xiami'}
		///Then:str = "<a href=/u/1>xiami</a>"
		///</summary>
		var result;
		var patt = new RegExp("%([a-zA-z0-9]+)%");
		while ((result = patt.exec(str)) != null) {
			var v = data[result[1]] || '';
			str = str.replace(new RegExp(result[0], "g"), v);
		}
		;
		return str;
	};
	$.vPager.prototype={
        /**
         * Calculate the maximum number of pages
         * @method
         * @return {Number}
         */
        numPages: function(){
            return Math.ceil(this.cntItem / this.opts.numPerPage);
        },
        /**
         * Calculate start and end point of pagination links depending on
         * curPage and num_display_entries.
         * @returns {Array}
         */
        getInterval: function(curPage){
            var ne_half = Math.floor(this.opts.numDisplay / 2);
            var np = this.numPages();
            var upper_limit = np - this.opts.numDisplay;
            var start = curPage > ne_half ? Math.max(Math.min(curPage - ne_half, upper_limit), 0) : 0;
            var end = curPage > ne_half ? Math.min(curPage + ne_half + (this.opts.numDisplay % 2), np) : Math.min(this.opts.numDisplay, np);
            return {
                start: start,
                end: end
            };
        },
		/**
         * Helper function for generating a single link (or a span tag if it's the current page)
         * @param {Number} pid The page id for the new item
         * @param {Number} curPage current page
         * @param {Object} data for the new item: text and classes
         * @param {String} link html template
         * @returns {jQuery} jQuery object containing the link
         */
        createLink: function(pid, curPage, data,tplLink){
            var lnk, np = this.numPages();
            pid = pid < 0 ? 0 : (pid < np ? pid : np - 1); // Normalize page id to sane value
            data = $.extend({
				id:pid,
                text: pid + 1,
                classes: "",
				clSpecialNow:false
            }, data || {});
			
			lnk=$($.vPager.ParseTpl(tplLink,data));
			
            if (pid == curPage){
				if (data.clSpecialNow === false) {
					lnk.addClass(this.opts.clNow);
				}else if(data.clSpecialNow){
					lnk.addClass(data.clSpecialNow);		
				};
            };
            if (data.classes.length>0) {
                lnk.addClass(data.classes);
            }
            lnk.data('page_id', pid);
            return lnk;
        },
        /**
         * Generate a range of numeric links
         * @param {Object} $t links container
         * @param {Object} curPage current page index
         * @param {Object} start
         * @param {Object} end
         * @param {Object} opts
         * @param {String} tplLink template string
         */ 
        appendRange: function($t, curPage, start, end, opts,tplLink){
            var i;
            for (i = start; i < end; i++) {
                this.createLink(i, curPage, opts,tplLink).appendTo($t);
            };
        },
		/**
		 * generate the html layout
		 * @param {Object} curPage current page	
		 * @param {Object} onClick	onClick callback
		 */
        genUI: function(curPage, onClick){
            var begin, end, interval = this.getInterval(curPage), np = this.numPages(),tempDiv=$("<div/>");
			
			//Generate "First" link
            if (this.opts.txtFirst|| this.opts.stickFirst) {
                tempDiv.append(this.createLink(0, curPage, {
                    text: this.opts.txtFirst,
                    classes:this.opts.clFirst,
					clSpecialNow:this.opts.clFirst+"_hot"
                },this.opts.tplFirst));
            };
            
            // Generate "Previous"-Link
            if (this.opts.txtPrev && (curPage > 0 || this.opts.stickPrev)) {
                tempDiv.append(this.createLink(curPage - 1, curPage, {
                    text: this.opts.txtPrev,
                    classes:this.opts.clPrev,
					clSpecialNow:this.opts.clPrev+"_hot"
                },this.opts.tplPrev));
            };
			
            // Generate starting points
            if (interval.start > 0 && this.opts.numEdge > 0) {
                end = Math.min(this.opts.numEdge, interval.start);
                this.appendRange(tempDiv, curPage, 0, end, {
                    classes:this.opts.clStartPoint
                },this.opts.tplLink);
                if (this.opts.numEdge < interval.start && this.opts.txtEllipse) {
					tempDiv.append(this.createLink(interval.start-1,curPage,{
						text:this.opts.txtEllipse,
						classes:this.opts.clEllipse,
						clSpecialNow:null
					},this.opts.tplEllipse));
                };
            }
            // Generate interval links
            this.appendRange(tempDiv, curPage, interval.start, interval.end,{},this.opts.tplLink);
            // Generate ending points
            if (interval.end < np && this.opts.numEdge > 0) {
                if (((np - this.opts.numEdge) > interval.end) && this.opts.txtEllipse) {
					tempDiv.append(this.createLink(interval.end+1, curPage, {
						text: this.opts.txtEllipse,
						classes: this.opts.clEllipse,
						clSpecialNow: null
					},this.opts.tplEllipse));
				};
                begin = Math.max(np - this.opts.numEdge, interval.end);
                this.appendRange(tempDiv, curPage, begin, np, {
                    classes:this.opts.clEndPoint
                },this.opts.tplLink);
                
            };
            // Generate "Next"-Link
            if (this.opts.txtNext && (curPage < np - 1 || this.opts.stickNext)) {
                tempDiv.append(this.createLink(curPage + 1, curPage, {
                    text: this.opts.txtNext,
                    classes:this.opts.clNext,
					clSpecialNow:this.opts.clNext+"_hot"
                },this.opts.tplNext));
            };
			
			// Last link
            if (this.opts.txtLast|| this.opts.stickLast) {
                tempDiv.append(this.createLink(np-1, curPage, {
                    text: this.opts.txtLast,
                    classes:this.opts.clLast,
					clSpecialNow:this.opts.clLast+"_hot"
                },this.opts.tplLast));
            };			
			
            $('a', tempDiv).click(onClick);
			
			if(this.opts.wrapUI) return tempDiv;
			
            return tempDiv.children();
        }		
	};
    
    // Extend jQuery
    $.fn.vPager = function(cntItem, opts){
    
        // Initialize options with default values
        opts = jQuery.extend({
            numPerPage: 10,
            numDisplay: 11,
            curPage: 0,
            numEdge: 0,
            linkTo: "#",
            txtPrev: "Prev",
            txtNext: "Next",
			txtFirst:'First',
			txtLast:'Last',
            txtEllipse: "...",
            stickPrev: true,
            stickNext: true,
			stickFirst:true,
			stickLast:true,
			tplLink:'<span><a href="javascript://">%text%</a></span>',
			tplPrev:'<span><a href="javascript://">%text%</a></span>',
			tplNext:'<span><a href="javascript://">%text%</a></span>',
			tplFirst:'<span><a href="javascript://">%text%</a></span>',
			tplLast:'<span><a href="javascript://">%text%</a></span>',
			tplEllipse:'<span><a href="javascript://">%text%</a></span>',
			clNow:'current',
			clPrev:'prev',
			clNext:'next',
			clFirst:'first',
			clLast:'last',
			clStartPoint:'sp',
			clEndPoint:'ep',
			clEllipse:'ellipse',
			wrapUI:true,
			runCallbackAfterInit:true,
            callback: function(){
                return false;
            }
        }, opts || {});
        
        var p={};
		//create a vPager factory class
		p.factory=new $.vPager(cntItem,opts);
		p.$t=this.data('curPage', opts.curPage);
		p.links=null;
		p.curPage=opts.curPage;
		opts.numPerPage = (!opts.numPerPage || opts.numPerPage < 0) ? 1 : opts.numPerPage;
		cntItem=(!cntItem || cntItem < 0) ? 1 : cntItem;
         
		 /**
         * This is a utility function for the internal event handlers.
         * It sets the new current page on the pagination container objects,
         * generates a new HTMl fragment for the pagination links and calls
         * the callback function.
         */
		p.selectPage=function(newPage){
            // update the link display of a all containers
            p.links = p.factory.genUI(newPage, p.onClick);
            p.$t.data('curPage', newPage).empty().append(p.links);
            // call the callback and propagate the event if it does not return false
            return opts.callback(newPage, p.$t);
		};
		
        /**
         * This is the internal event handling function for the pagination links.
         */
        p.onClick=function(evt){
            var newPage = $(evt.target).data('page_id'), goOn = p.selectPage(newPage);
            if (!goOn) {
                evt.stopPropagation();
            }
            return goOn;
        };
   
        // Attach control events to the DOM elements
        p.np = p.factory.numPages();
        p.$t.bind('setPage', {
            numPages: p.np
        }, function(evt, pid){
            if (pid >= 0 && pid < evt.data.numPages) {
                p.selectPage(pid);
                return false;
            };
        }).bind('prevPage', function(evt){
            var curPage = $(this).data('curPage');
            if (curPage > 0) {
                p.selectPage(curPage - 1);
            };
            return false;
        }).bind('nextPage', {
            numPages: p.np
        }, function(evt){
            var curPage = $(this).data('curPage');
            if (curPage < evt.data.numPages - 1) {
                p.selectPage(curPage + 1);
            };
            return false;
        });
        
        // When all initialisation is done, draw the links
        p.links =p.factory.genUI(p.curPage,p.onClick);
        p.$t.empty().append(p.links);
        // call callback function
		if(opts.runCallbackAfterInit)
        	opts.callback(p.curPage, p.$t);
    }; // End of $.fn.vPager block

})(jQuery);
